# -*- python -*-

import os
import subprocess
import string
import re

def build_dbus_glue(target, source, env):
    """
    C++ doesn't allow casting from void* to a function pointer,
    thus we have to change the code to use a union to do the
    conversion.
    """
    xml = subprocess.Popen(["dbus-binding-tool",
                            "--mode=glib-server",
                            "--prefix=" + env['DBUS_PREFIX'], source[0].get_path()],
                           stdout=subprocess.PIPE).communicate()[0]

    xml = re.sub(r"callback = \(([A-Za-z_]+)\) \(marshal_data \? marshal_data : cc->callback\);",
                 r"union { \1 fn; void* obj; } conv;\n  "
                 "conv.obj = (marshal_data ? marshal_data : cc->callback);\n  "
                 "callback = conv.fn;", xml)

    with open(target[0].get_path(), "w") as f:
        f.write(xml)

def build_bin2h(target, source, env):
    """
    Takes a list of files and converts them into a C source that can be included
    """
    def c_escape(str): 
        return str.translate(string.maketrans("/.-", "___"))
    
    print target
    print source
    with open(target[0].get_path(), "w") as fout:
        fout.write("// autogenerated by scons Bin2H builder, do not edit by hand!\n\n")

        if env.has_key("BIN2H_NAMESPACE"):
            fout.write("namespace %s {\n\n" % env["BIN2H_NAMESPACE"])
            
        # write down data
        for src in source:
            with open(src.get_path(), "rb") as fin:
                data = fin.read()
                fout.write("// \"%s\"\n" % src.get_path())
                fout.write("const char %s[] = {" % c_escape(src.get_path()))
                bytes_arr = ["0x%02x" % ord(c) for c in data]
                for i in xrange(len(bytes_arr)):
                    if i % 13 == 0:
                        fout.write("\n  ")
                    fout.write(bytes_arr[i])
                    if i != len(bytes_arr)-1:
                        fout.write(", ")
                fout.write("\n};\n\n")

        # write down file table
        if False:
            fout.write("const char** file_table = {\n")
            fout.write(string.join(["  %-35s %-s" % ("\"%s\"," % src.get_path(),
                                                     c_escape(src.get_path()))
                                    for src in source], ",\n"))
            fout.write("\n}\n\n")

        if env.has_key("BIN2H_NAMESPACE"):
            fout.write("} // namespace %s\n\n" % env["BIN2H_NAMESPACE"])
                
        fout.write("/* EOF */\n")
                

env = Environment(ENV=os.environ, BUILDERS = {
    'DBusGlue' : Builder(action = build_dbus_glue),
    'Bin2H'    : Builder(action = build_bin2h)
    })

opts = Variables(['custom.py'], ARGUMENTS)

opts.Add('CPPPATH', 'Additional preprocessor paths')
opts.Add('CPPFLAGS', 'Additional preprocessor flags')
opts.Add('CPPDEFINES', 'defined constants')
opts.Add('LIBPATH', 'Additional library paths')
opts.Add('LIBS', 'Additional libraries')
opts.Add('CCFLAGS', 'C Compiler flags')
opts.Add('CXXFLAGS', 'C++ Compiler flags')
opts.Add('LINKFLAGS', 'Linker Compiler flags')
opts.Add('AR', 'Library archiver')
opts.Add('CC', 'C Compiler')
opts.Add('CXX', 'C++ Compiler')
opts.Add('BUILD', 'Build type: release, custom, development')
opts.Add('PKG_CONFIG', 'pkg-config helper tool', 'pkg-config')

opts.Update(env)
Help(opts.GenerateHelpText(env))

env.Append(CPPPATH=["src/"])

if 'BUILD' in env and env['BUILD'] == 'development':
    env.Append(CXXFLAGS = [ "-O3",
                            "-g3",
                            "-ansi",
                            "-pedantic",
                            "-Wall",
                            "-Wextra",
                            "-Werror",
                            "-Wnon-virtual-dtor",
                            "-Weffc++",
                            # "-Wunreachable-code",
                            # "-Wconversion",
                            "-Wold-style-cast",
                            "-Wshadow",
                            "-Wcast-qual",
                            "-Winit-self", # only works with >= -O1
                            "-Wno-unused-parameter"])
elif 'BUILD' in env and env['BUILD'] == 'custom':
    pass
else:
    env.Append(CPPFLAGS = ['-g', '-O3', '-Wall', '-ansi', '-pedantic'])

env.ParseConfig(env['PKG_CONFIG'] + " --cflags --libs dbus-glib-1 | sed 's/-I/-isystem/g'")
env.ParseConfig(env['PKG_CONFIG'] + " --cflags --libs glib-2.0 | sed 's/-I/-isystem/g'")
env.ParseConfig(env['PKG_CONFIG'] + " --cflags --libs gthread-2.0 | sed 's/-I/-isystem/g'")
env.ParseConfig(env['PKG_CONFIG'] + " --cflags --libs libusb-1.0 | sed 's/-I/-isystem/g'")
env.ParseConfig(env['PKG_CONFIG'] + " --cflags --libs libudev | sed 's/-I/-isystem/g'")

f = open("VERSION")
package_version = f.read()
f.close()
    
env.Append(CPPDEFINES = { 'PACKAGE_VERSION': "'\"%s\"'" % package_version })

conf = Configure(env)

if not conf.env['CXX']:
    print "g++ must be installed!"
    Exit(1)

# X11 checks
if not conf.CheckLibWithHeader('X11', 'X11/Xlib.h', 'C++'):
    print 'libx11-dev must be installed!'
    Exit(1)

env = conf.Finish()

env.Bin2H("src/xboxdrv_vfs.hpp", [
    "examples/mouse.xboxdrv",
    "examples/xpad-wireless.xboxdrv"
    ],
          BIN2H_NAMESPACE="xboxdrv_vfs")
env.DBusGlue("src/xboxdrv_daemon_glue.hpp",     "src/xboxdrv_daemon.xml", DBUS_PREFIX="xboxdrv_daemon")
env.DBusGlue("src/xboxdrv_controller_glue.hpp", "src/xboxdrv_controller.xml", DBUS_PREFIX="xboxdrv_controller")

libxboxdrv = env.StaticLibrary('xboxdrv',
                               Glob('src/*.cpp') +
                               Glob('src/axisfilter/*.cpp') +
                               Glob('src/buttonfilter/*.cpp') +
                               Glob('src/axisevent/*.cpp') +
                               Glob('src/buttonevent/*.cpp') +
                               Glob('src/modifier/*.cpp'))
env.Prepend(LIBS = libxboxdrv)

for file in Glob('test/*_test.cpp', strings=True):
    Alias('tests', env.Program(file[:-4], file))

Default(env.Program('xboxdrv', Glob('src/main/main.cpp')))

# EOF #
